package org.listbeast.core;

import java.io.File;
import java.util.ArrayList;
import java.util.List;


import org.apache.log4j.Logger;
import org.listbeast.core.common.ProcessingException;
import org.listbeast.core.common.ValidationException;
import org.listbeast.core.dataobjects.DiskSong;
import org.listbeast.core.dataobjects.SongCollection;
import org.listbeast.core.dataobjects.YoutubeSong;
import org.listbeast.core.datasource.impl.FileSystemSongReader;
import org.listbeast.core.datasource.impl.YoutubeSongReader;
import org.listbeast.core.filters.SongFilter;
import org.listbeast.core.filters.algorithms.GeneralMatchingAlgorithm;
import org.listbeast.core.observer.ListBeastAppObservable;
import org.listbeast.core.observer.ListBeastGUIObserver;
import org.listbeast.core.observer.NotificationData;
import org.listbeast.core.observer.NotificationType;
import org.listbeast.core.playlist.PlaylistHandler;


public class ListBeastApp implements Runnable, ListBeastAppObservable {

	private static final Logger LOG = Logger.getLogger(ListBeastApp.class);
	private final String targetDir;
	private final String sourceDir;
	private final int maxYoutubeResults;
	private final boolean copySongs;
	private final boolean isMultiplePlaylists;
	private final List<ListBeastGUIObserver> observers = new ArrayList<ListBeastGUIObserver>();
	
	public ListBeastApp(String targetDir, String sourceDir, int maxYoutubeResults, boolean copySongs, boolean isMultiplePlaylists) throws ValidationException {

		this.targetDir = targetDir;
		this.sourceDir = sourceDir;
		this.maxYoutubeResults = maxYoutubeResults;
		this.copySongs = copySongs;
		this.isMultiplePlaylists = isMultiplePlaylists;
		validateInput();
		logProps();
	}

	private void validateInput() {
		File file = new File(sourceDir);
		if (!file.isDirectory() || !file.canRead()) {
			throw new ValidationException("Cannot access source directory. "
					+ sourceDir);
		}
		if (maxYoutubeResults <= 0) {
			throw new ValidationException("The number of maximum youtube results per artist should be greater than zero.");
		}
	}

	private void logProps() {
		LOG.info("Target directory: " + targetDir);
		LOG.info("Source directory: " + sourceDir);
		LOG.info("Maximum Youtube Results: " + maxYoutubeResults);
		LOG.info("Copy selected songs: " + copySongs);
	}

	private void startApplication() {

		final long startTime = System.nanoTime();

		LOG.info("Application started");
		
		notifyGUIObservers(NotificationType.APP_STARTED, new NotificationData("loading music library"));

		try {
			// load music library
			FileSystemSongReader fsReader = new FileSystemSongReader(sourceDir);
			SongCollection<DiskSong> diskSongs = fsReader.getSongs();
			notifyGUIObservers(NotificationType.UPDATE_PROGRESS_BAR, new NotificationData(40, "fetching youtube song titles"));

			// fetch youtube titles for every artist in my collection
			GeneralMatchingAlgorithm songMatchingAlgorithm = new GeneralMatchingAlgorithm();
			SongFilter filter = new SongFilter(songMatchingAlgorithm);
			YoutubeSongReader youtubeReader = new YoutubeSongReader(maxYoutubeResults, diskSongs.getArtistNames(), filter);
			SongCollection<YoutubeSong> youtubeSongs = youtubeReader.getSongs();
			notifyGUIObservers(NotificationType.UPDATE_PROGRESS_BAR, new NotificationData(60, "comparing youtube titles with library songs"));

			// match library songs with youtube titles
			SongCollection<DiskSong> matchedDiskSongs = filter.findMatches(diskSongs, youtubeSongs);
			notifyGUIObservers(NotificationType.UPDATE_PROGRESS_BAR, new NotificationData(90, "finalizing"));
			
			// write playlists and execute further finalizations
			PlaylistHandler plHandler = new PlaylistHandler(targetDir);
			plHandler.createPlaylists(matchedDiskSongs, isMultiplePlaylists);

			if (copySongs) {
				plHandler.copySongs(matchedDiskSongs);
			}
			notifyGUIObservers(NotificationType.APP_FINISHED, new NotificationData("Finished!"));
		} finally {
			final long endTime = System.nanoTime();
			final long duration = endTime - startTime;

			LOG.info("Duration of Execution: " + duration / 1000000000
					+ " secs");
		}

		/*
		 * Uncomment when didyoumean code snippet is ready if
		 * (selectedSongs.values().size() < maxResults) { //check youtube
		 * didyoumean proposal
		 * log.info("Checking DID-YOU-MEAN for artist: "+artistName); String
		 * didyoumean = youtubeClient.checkDidYouMean(artistName); if
		 * ((didyoumean != null) && (!didyoumean.isEmpty())) {
		 * log.info("Found DID-YOU-MEAN: "+didyoumean); youtubeVideoTitles =
		 * youtubeClient.getVideos(didyoumean, maxResults); HashMap<String,
		 * String> selectedSongs2 = filter.selectSongs(didyoumean,
		 * youtubeVideoTitles, musicLibrary.get(artistName));
		 * selectedArtistSongs.put(didyoumean, selectedSongs); } else {
		 * log.info("Found NO DID-YOU-MEAN for artist: "+artistName); } }
		 */

	}

	public void run() {
		startApplication();
	}

	public void registerObserver(ListBeastGUIObserver observer) {
		observers.add(observer);
	}

	public void removeObserver(ListBeastGUIObserver observer) {
		observers.remove(observer);
	}

	public void notifyGUIObservers(NotificationType notificationType, NotificationData notificationData) {
		for (ListBeastGUIObserver observer : observers) {
			observer.update(notificationType, notificationData);
		}
	}
}
